﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ConstrainedExecution;
using System.Text.Json.Serialization;
using LightCAD.MathLib;

namespace LightCAD.Core.Elements
{
    /// <summary>
    /// 角度标注
    /// </summary>
    public class DimAngularArc : LcElement
    {
        public List<LcLine> selectLines;
        public LcArc selectArc;
        public double Radius;
        public double StartAngle;
        public double EndAngle;
        public double MoveAngle;
        public Vector2 Startp;
        public Vector2 Endp;
        public Vector2 Movep;
        public Vector2 Textp;
        public Vector2 Midp;
        public Vector2 Centerp;

        public LcText Dimtext;
        public DimAngularArc()
        {
            this.Type = BuiltinElementType.DimAngularArc;
        }

        public DimAngularArc(Vector2 one) : this()
        {

        }

        public void LoadDimAngleProperty() {
            #region 选中2条线画角度标注
            if (selectLines.Count == 2)
            {
                if (Centerp != null)
                {
                    Radius = Math.Sqrt(Math.Pow(Centerp.X - Movep.X, 2) + Math.Pow(Centerp.Y - Movep.Y, 2));
                    //获取2条线交叉的4个向量
                    List<Vector2> veclist = new List<Vector2>() {
                        selectLines[0].End - selectLines[0].Start,
                        selectLines[0].Start - selectLines[0].End,
                        selectLines[1].End - selectLines[1].Start,
                        selectLines[1].Start - selectLines[1].End,
                    };
                    //新的4个点位用来生成角度
                    List<Vector2> pointlist = new List<Vector2>();
                    foreach (var item in veclist)
                    {
                        var movevec = (new Vector2(item.X * 100, item.Y * 100)) / Math.Sqrt(Math.Abs((Math.Pow(item.X, 2.0) + Math.Pow(item.Y, 2.0))));
                        pointlist.Add(Centerp + movevec);
                    }
                    //根据向量获取4个角度
                    List<double> anglelist = new List<double>() {
                        ThreePointGetAngle(new Vector2(Centerp.X + 100, Centerp.Y), Centerp, pointlist[0], true),
                        ThreePointGetAngle(new Vector2(Centerp.X + 100, Centerp.Y), Centerp, pointlist[1], true),
                        ThreePointGetAngle(new Vector2(Centerp.X + 100, Centerp.Y), Centerp, pointlist[2], true),
                        ThreePointGetAngle(new Vector2(Centerp.X + 100, Centerp.Y), Centerp, pointlist[3], true),
                    };
                    StartAngle = 0;
                    EndAngle = 0;
                    MoveAngle = 0;
                    double mpangle = ThreePointGetAngle(new Vector2(Centerp.X + 100, Centerp.Y), Centerp, Movep, true);
                    List<double> anglesort = anglelist.OrderByDescending(x => x).ToList();
                    if (mpangle > 0)
                    {
                        if (mpangle < anglesort[1])
                        {
                            StartAngle = anglesort[2];
                            EndAngle = anglesort[1];
                            MoveAngle = -StartAngle + EndAngle;
                        }
                        else if (mpangle > anglesort[1] && mpangle < anglesort[0])
                        {
                            StartAngle = anglesort[1];
                            EndAngle = anglesort[0];
                            MoveAngle = EndAngle - StartAngle;
                        }
                        else if (mpangle > anglesort[0])
                        {
                            StartAngle = anglesort[0];
                            EndAngle = anglesort[3];
                            MoveAngle = 360 - StartAngle + EndAngle;
                        }
                    }
                    else
                    {
                        if (mpangle > anglesort[2])
                        {
                            StartAngle = anglesort[2];
                            EndAngle = anglesort[1];
                            MoveAngle = EndAngle - StartAngle;
                        }
                        else if (mpangle > anglesort[3] && mpangle < anglesort[2])
                        {
                            StartAngle = anglesort[3];
                            EndAngle = anglesort[2];
                            MoveAngle = EndAngle - StartAngle;
                        }
                        else if (mpangle < anglesort[3])
                        {
                            StartAngle = anglesort[0];
                            EndAngle = anglesort[3];
                            MoveAngle = 360 - StartAngle + EndAngle;
                        }
                    }
                    Startp = PointRotate(Centerp, new Vector2(Centerp.X + Radius, Centerp.Y), StartAngle);
                    Endp = PointRotate(Centerp, new Vector2(Centerp.X + Radius, Centerp.Y), EndAngle);

                    var a = Startp - Centerp;
                    var b = Endp - Centerp;
                    var m = a + b;
                    m.Normalize();
                    m = m * Radius;
                    this.Midp = Centerp + m;
                    if (Math.Abs(this.MoveAngle) > 180)
                    {
                        this.Midp = Vector2.Rotate(this.Midp, this.Centerp, 180);
                    }
                }
            }
            #endregion
        }

        /// <summary>
        /// 对一个坐标点按照一个中心进行旋转
        /// </summary>
        /// <param name="center">中心点</param>
        /// <param name="p1">要旋转的点</param>
        /// <param name="angle">旋转角度，笛卡尔直角坐标</param>
        /// <returns></returns>
        private Vector2 PointRotate(Vector2 center, Vector2 p1, double angle)
        {
            Vector2 tmp = new Vector2();
            double angleHude = angle * Math.PI / 180;/*角度变成弧度*/
            double x1 = (p1.X - center.X) * Math.Cos(angleHude) + (p1.Y - center.Y) * Math.Sin(angleHude) + center.X;
            double y1 = -(p1.X - center.X) * Math.Sin(angleHude) + (p1.Y - center.Y) * Math.Cos(angleHude) + center.Y;
            tmp.X = (int)x1;
            tmp.Y = (int)y1;
            return tmp;
        }

        /// <summary>
        /// 三点确定角度
        /// </summary>
        /// <param name="startpoint"></param>
        /// <param name="centerpoint"></param>
        /// <param name="endpoint"></param>
        /// <param name="reversal">是否开启顺时针180为正，逆时针180为负,默认为逆时针负角度</param>
        /// <returns></returns>
        public double ThreePointGetAngle(Vector2 startpoint, Vector2 centerpoint, Vector2 endpoint, bool reversal = false)
        {
            double angle = 0;
            var so = new Vector2(startpoint.X - centerpoint.X, startpoint.Y - centerpoint.Y);
            var eo = new Vector2(endpoint.X - centerpoint.X, endpoint.Y - centerpoint.Y);
            var pddir = (so.X * eo.Y) - (so.Y * eo.X);
            angle = (so.Angle() - eo.Angle()) / Math.PI * 180;
            if (reversal)
            {
                angle = (so.Angle() + (pddir < 0 ? (2 * Math.PI - eo.Angle()) : -eo.Angle())) / Math.PI * 180;
            }

            return angle;
        }

        public override Box2 GetBoundingBox()
        {
            return new Box2().SetFromPoints(this.Startp, this.Endp);
        }
        public override Box2 GetBoundingBox(Matrix3 matrix)
        {
            return new Box2().SetFromPoints(matrix.MultiplyPoint(this.Startp), matrix.MultiplyPoint(this.Endp));
        }
        public override bool IntersectWithBox(Polygon2d testPoly, List<RefChildElement> intersectChildren = null)
        {
            var thisBox = this.BoundingBox;
            if (!thisBox.IntersectsBox(testPoly.BoundingBox))
            {
                //如果元素盒子，与多边形盒子不相交，那就可能不相交
                return false;
            }
            return GeoUtils.IsPolygonIntersectLine(testPoly.Points, this.Startp, this.Endp)
                || GeoUtils.IsPolygonContainsLine(testPoly.Points, this.Startp, this.Endp);
        }

        public override bool IncludedByBox(Polygon2d testPoly, List<RefChildElement> includedChildren = null)
        {
            var thisBox = this.BoundingBox;
            if (!thisBox.IntersectsBox(testPoly.BoundingBox))
            {
                return false;
            }
            return GeoUtils.IsPolygonContainsLine(testPoly.Points, this.Startp, this.Endp);
        }
        public override LcElement Clone()
        {
            var clone = new DimAngularArc();
            clone.Copy(this);
            return clone;
        }

        public override void Copy(LcElement src)
        {
            var dim = ((DimAngularArc)src);
        }

        public void Set(Vector2 start = null, Vector2 end = null, bool fireChangedEvent = true)
        {
            
        }

        /// <summary>
        /// 获取两条直线的交点
        /// </summary>
        /// <param name="line1"></param>
        /// <param name="line2"></param>
        /// <returns></returns>
        public static Vector2 GetLineIntersection(LcLine line1, LcLine line2)
        {
            var lineFirstStar = line1.Start;
            var lineFirstEnd = line1.End;
            var lineSecondStar = line2.Start;
            var lineSecondEnd = line2.End;

            return GetLineIntersection(lineFirstStar, lineFirstEnd, lineSecondStar, lineSecondEnd);
        }

        /// <summary>
        /// 获取两条直线的交点
        /// </summary>
        /// <param name="lineFirstStar"></param>
        /// <param name="lineFirstEnd"></param>
        /// <param name="lineSecondStar"></param>
        /// <param name="lineSecondEnd"></param>
        /// <returns></returns>
        public static Vector2 GetLineIntersection(Vector2 lineFirstStar, Vector2 lineFirstEnd, Vector2 lineSecondStar, Vector2 lineSecondEnd)
        {
            /*
             * L1，L2都存在斜率的情况：
             * 直线方程L1: ( y - y1 ) / ( y2 - y1 ) = ( x - x1 ) / ( x2 - x1 ) 
             * => y = [ ( y2 - y1 ) / ( x2 - x1 ) ]( x - x1 ) + y1
             * 令 a = ( y2 - y1 ) / ( x2 - x1 )
             * 有 y = a * x - a * x1 + y1   .........1
             * 直线方程L2: ( y - y3 ) / ( y4 - y3 ) = ( x - x3 ) / ( x4 - x3 )
             * 令 b = ( y4 - y3 ) / ( x4 - x3 )
             * 有 y = b * x - b * x3 + y3 ..........2
             * 
             * 如果 a = b，则两直线平等，否则， 联解方程 1,2，得:
             * x = ( a * x1 - b * x3 - y1 + y3 ) / ( a - b )
             * y = a * x - a * x1 + y1
             * 
             * L1存在斜率, L2平行Y轴的情况：
             * x = x3
             * y = a * x3 - a * x1 + y1
             * 
             * L1 平行Y轴，L2存在斜率的情况：
             * x = x1
             * y = b * x - b * x3 + y3
             * 
             * L1与L2都平行Y轴的情况：
             * 如果 x1 = x3，那么L1与L2重合，否则平等
             * 
            */
            double a = 0, b = 0;
            var state = 0;
            if (Math.Abs(lineFirstStar.X - lineFirstEnd.X) > 0.1)// (lineFirstStar.X != lineFirstEnd.X)
            {
                a = (lineFirstEnd.Y - lineFirstStar.Y) / (lineFirstEnd.X - lineFirstStar.X);
                state |= 1;
            }
            if (Math.Abs(lineSecondStar.X - lineSecondEnd.X) > 0.1) //(lineSecondStar.X != lineSecondEnd.X)
            {
                b = (lineSecondEnd.Y - lineSecondStar.Y) / (lineSecondEnd.X - lineSecondStar.X);
                state |= 2;
            }
            switch (state)
            {
                case 0: //L1与L2都平行Y轴
                    {
                        if (lineFirstStar.X == lineSecondStar.X)
                        {
                            //throw new Exception("两条直线互相重合，且平行于Y轴，无法计算交点。");
                            return null;
                        }
                        else
                        {
                            //throw new Exception("两条直线互相平行，且平行于Y轴，无法计算交点。");
                            return null;
                        }
                    }
                case 1: //L1存在斜率, L2平行Y轴
                    {
                        var x = lineSecondStar.X;
                        var y = (lineFirstStar.X - x) * (-a) + lineFirstStar.Y;
                        return new Vector2(x, y);
                    }
                case 2: //L1 平行Y轴，L2存在斜率
                    {
                        var x = lineFirstStar.X;
                        //网上有相似代码的，这一处是错误的。你可以对比case 1 的逻辑 进行分析
                        //源code:lineSecondStar * x + lineSecondStar * lineSecondStar.X + p3.Y;
                        var y = (lineSecondStar.X - x) * (-b) + lineSecondStar.Y;
                        return new Vector2(x, y);
                    }
                case 3: //L1，L2都存在斜率
                    {
                        if (a == b)
                        {
                            // throw new Exception("两条直线平行或重合，无法计算交点。");
                            return null;
                        }
                        var x = (a * lineFirstStar.X - b * lineSecondStar.X - lineFirstStar.Y + lineSecondStar.Y) / (a - b);
                        var y = a * x - a * lineFirstStar.X + lineFirstStar.Y;
                        return new Vector2(x, y);
                    }
            }
            // throw new Exception("不可能发生的情况");
            return null;
        }
    }
}
